/* bzflag
 * Copyright (c) 1993-2016 Tim Riker
 *
 * embedded port changes
 * Copyright (c) 2015-2016 Joshua Bodine
 *
 * This package is free software;  you can redistribute it and/or
 * modify it under the terms of the license found in the file
 * named COPYING that should have accompanied this file.
 *
 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

// bzflag common header
#include "common.h"

// interface header
#include "BoltSceneNode.h"

// system headers
#include <stdlib.h>
#include <math.h>

// common implementation headers
#include "StateDatabase.h"
#include "BZDBCache.h"
#include "TextureManager.h"

// local implementation headers
#include "ViewFrustum.h"

// FIXME (SceneRenderer.cxx is in src/bzflag)
#include "SceneRenderer.h"

#include "TimeKeeper.h"

BoltSceneNode::BoltSceneNode(const GLfloat pos[3],const GLfloat vel[3], bool super) :
				isSuper(super),
				invisible(false),
				drawFlares(false),
				texturing(false),
				colorblind(false),
				size(1.0f),
				renderNode(this),
				azimuth(0),
				elevation(0),
				length(1.0f)
{

  OpenGLGStateBuilder builder(gstate);
  builder.setBlending();
  builder.setAlphaFunc();
  //builder.setTextureEnvMode(GL_DECAL);
  gstate = builder.getState();

  // prepare light
  light.setAttenuation(0, 0.05f);
  light.setAttenuation(1, 0.0f);
  light.setAttenuation(2, 0.03f);

  // prepare geometry
  move(pos, vel);
  setSize(size);
  setColor(1.0f, 1.0f, 1.0f);
  teamColor = fvec4(1,1,1,1);
}

BoltSceneNode::~BoltSceneNode()
{
  // do nothing
}

void			BoltSceneNode::setFlares(bool on)
{
  drawFlares = on;
}

void			BoltSceneNode::setSize(float radius)
{
  size = radius;
  setRadius(size * size);
}
void			BoltSceneNode::setTextureColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a)
{
  color[0] = r;
  color[1] = g;
  color[2] = b;
  color[3] = a;
  light.setColor(1.5f * r, 1.5f * g, 1.5f * b);
  renderNode.setTextureColor(color);
}

void			BoltSceneNode::setColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a)
{
  color[0] = r;
  color[1] = g;
  color[2] = b;
  color[3] = a;
  light.setColor(1.5f * r, 1.5f * g, 1.5f * b);
  renderNode.setColor(color);
}

void			BoltSceneNode::setTeamColor(const GLfloat *c)
{
	teamColor.r = c[0];
	teamColor.g = c[1];
	teamColor.b = c[2];
	teamColor.w = 1.0f;
}

void			BoltSceneNode::setColor(const GLfloat* rgb)
{
  setColor(rgb[0], rgb[1], rgb[2]);
}

bool			BoltSceneNode::getColorblind() const
{
  return colorblind;
}

void			BoltSceneNode::setColorblind(bool _colorblind)
{
  colorblind = _colorblind;
}

void			BoltSceneNode::setTexture(const int texture)
{
  OpenGLGStateBuilder builder(gstate);
  builder.setTexture(texture);
  builder.enableTexture(texture>=0);
  gstate = builder.getState();
}

void			BoltSceneNode::setTextureAnimation(int cu, int cv)
{
  renderNode.setAnimation(cu, cv);
}

void			BoltSceneNode::move(const GLfloat pos[3],
							const GLfloat vel[3])
{
  setCenter(pos);
  light.setPosition(pos);
  velocity[0] = vel[0];
  velocity[1] = vel[1];
  velocity[2] = vel[2];
  length = sqrtf((vel[0] * vel[0]) +
		 (vel[1] * vel[1]) +
		 (vel[2] * vel[2]));

  azimuth   = (float)(+RAD2DEG * atan2f(vel[1], vel[0]));
  elevation = (float)(-RAD2DEG * atan2f(vel[2], sqrtf(vel[0]* vel[0] + vel[1] *vel[1])));
}

void			BoltSceneNode::addLight(
				SceneRenderer& renderer)
{
  renderer.addLight(light);
}

void			BoltSceneNode::notifyStyleChange()
{
  texturing = BZDBCache::texture && BZDBCache::blend;
  OpenGLGStateBuilder builder(gstate);
  builder.enableTexture(texturing);
  if (BZDBCache::blend) {
    const int shotLength = (int)(BZDBCache::shotLength * 3.0f);
    if (shotLength > 0 && !drawFlares) {
      builder.setBlending(GL_SRC_ALPHA, GL_ONE);
    } else {
      builder.setBlending(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    }
    builder.setStipple(1.0f);
    builder.setAlphaFunc();
	if ((RENDERER.useQuality() >= 3) && drawFlares){
		builder.setShading(GL_SMOOTH);
		builder.enableMaterial(false);
	}else
		builder.setShading(texturing ? GL_FLAT : GL_SMOOTH);
  }
  else {
    builder.resetBlending();
    builder.resetAlphaFunc();
    builder.setStipple(0.5f);
    builder.setShading(GL_FLAT);
  }
  gstate = builder.getState();
}

void			BoltSceneNode::addRenderNodes(
				SceneRenderer& renderer)
{
  renderer.addRenderNode(&renderNode, &gstate);
}

//
// BoltSceneNode::BoltRenderNode
//

const GLfloat		BoltSceneNode::BoltRenderNode::CoreFraction = 0.4f;
const GLfloat		BoltSceneNode::BoltRenderNode::FlareSize = 1.0f;
const GLfloat		BoltSceneNode::BoltRenderNode::FlareSpread = 0.08f;
GLfloat			BoltSceneNode::BoltRenderNode::core[9][2];
GLfloat			BoltSceneNode::BoltRenderNode::corona[8][2];
const GLfloat		BoltSceneNode::BoltRenderNode::ring[8][2] = {
				{ 1.0f, 0.0f },
				{ (float)M_SQRT1_2, (float)M_SQRT1_2 },
				{ 0.0f, 1.0f },
				{ (float)-M_SQRT1_2, (float)M_SQRT1_2 },
				{ -1.0f, 0.0f },
				{ (float)-M_SQRT1_2, (float)-M_SQRT1_2 },
				{ 0.0f, -1.0f },
				{ (float)M_SQRT1_2, (float)-M_SQRT1_2 }
			};

BoltSceneNode::BoltRenderNode::BoltRenderNode(
				const BoltSceneNode* _sceneNode) :
				sceneNode(_sceneNode),
				numFlares(0)
{
  // initialize core and corona if not already done
  static bool init = false;
  if (!init) {
    init = true;
    core[0][0] = 0.0f;
    core[0][1] = 0.0f;
    for (int i = 0; i < 8; i++) {
      core[i+1][0] = CoreFraction * ring[i][0];
      core[i+1][1] = CoreFraction * ring[i][1];
      corona[i][0] = ring[i][0];
      corona[i][1] = ring[i][1];
    }
  }

  textureColor[0] = 1.0f;
  textureColor[1] = 1.0f;
  textureColor[2] = 1.0f;
  textureColor[3] = 1.0f;

  setAnimation(1, 1);
}

BoltSceneNode::BoltRenderNode::~BoltRenderNode()
{
  // do nothing
}

void			BoltSceneNode::BoltRenderNode::setAnimation(
				int _cu, int _cv)
{
  cu = _cu;
  cv = _cv;
  du = 1.0f / (float)cu;
  dv = 1.0f / (float)cv;

  // pick a random start frame
  const int index = (int)((float)cu * (float)cv * bzfrand());
  u = index % cu;
  v = index / cu;
  if (v >= cv) v = 0;
}
void			BoltSceneNode::BoltRenderNode::setTextureColor(const GLfloat* rgba)
{
  textureColor[0] = rgba[0];
  textureColor[1] = rgba[1];
  textureColor[2] = rgba[2];
  textureColor[3] = rgba[3];
}


void			BoltSceneNode::BoltRenderNode::setColor(
				const GLfloat* rgba)
{
  mainColor[0] = rgba[0];
  mainColor[1] = rgba[1];
  mainColor[2] = rgba[2];
  mainColor[3] = rgba[3];

  innerColor[0] = mainColor[0] + 0.5f * (1.0f - mainColor[0]);
  innerColor[1] = mainColor[1] + 0.5f * (1.0f - mainColor[1]);
  innerColor[2] = mainColor[2] + 0.5f * (1.0f - mainColor[2]);
  innerColor[3] = rgba[3];

  outerColor[0] = mainColor[0];
  outerColor[1] = mainColor[1];
  outerColor[2] = mainColor[2];
  outerColor[3] = (rgba[3] == 1.0f )? 0.1f: rgba[3];

  coronaColor[0] = mainColor[0];
  coronaColor[1] = mainColor[1];
  coronaColor[2] = mainColor[2];
  coronaColor[3] = (rgba[3] == 1.0f )? 0.5f : rgba[3];

  flareColor[0] = mainColor[0];
  flareColor[1] = mainColor[1];
  flareColor[2] = mainColor[2];
  flareColor[3] = (rgba[3] == 1.0f )? 0.667f : rgba[3];
}

void drawFin ( float maxRad, float finRadius, float boosterLen, float finForeDelta, float finCapSize)
{
  GLfloat drawArray[] = {
    1.0f, 0.0f, 0.0f,
    0, maxRad, 0,
    1.0f, 0.0f, 0.0f,
    0, maxRad, boosterLen,
    1.0f, 0.0f, 0.0f,
    0, maxRad+finRadius, boosterLen-finForeDelta,
    1.0f, 0.0f, 0.0f,
    0, maxRad+finRadius, boosterLen-finForeDelta-finCapSize,

    -1.0f, 0.0f, 0.0f,
    0.0f, maxRad+finRadius, boosterLen-finForeDelta-finCapSize,
    -1.0f, 0.0f, 0.0f,
    0.0f, maxRad+finRadius, boosterLen-finForeDelta,
    -1.0f, 0.0f, 0.0f,
    0.0f, maxRad, boosterLen,
    -1.0f, 0.0f, 0.0f,
    0.0f, maxRad, 0.0f
  };

  glDisableClientState(GL_COLOR_ARRAY);
  glDisableClientState(GL_TEXTURE_COORD_ARRAY);
  glEnableClientState(GL_NORMAL_ARRAY);
  glEnableClientState(GL_VERTEX_ARRAY);

  glNormalPointer(GL_FLOAT, 6 * sizeof(GLfloat), drawArray);
  glVertexPointer(3, GL_FLOAT, 6 * sizeof(GLfloat), drawArray + 3);

  glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
  glDrawArrays(GL_TRIANGLE_FAN, 4, 4);
}

void BoltSceneNode::BoltRenderNode::renderGeoGMBolt()
{
	// bzdb these 2? they control the shot size
	float gmMissleSize = BZDBCache::gmSize;

	// parametrics
	float maxRad = gmMissleSize * 0.16f;
	float noseRad = gmMissleSize * 0.086f;
	float waistRad = gmMissleSize * 0.125f;
	float engineRad = gmMissleSize * 0.1f;

	float noseLen = gmMissleSize * 0.1f;
	float bodyLen = gmMissleSize * 0.44f;
	float bevelLen = gmMissleSize * 0.02f;
	float waistLen = gmMissleSize * 0.16f;
	float boosterLen = gmMissleSize * 0.2f;
	float engineLen = gmMissleSize * 0.08f;

	float finRadius = gmMissleSize * 0.16f;
	float finCapSize = gmMissleSize * 0.15f;
	float finForeDelta = gmMissleSize * 0.02f;

	int slices = 8;

	float rotSpeed = 90.0f;

	glDepthMask(GL_TRUE);
	glPushMatrix();
	glRotatef(sceneNode->azimuth, 0.0f, 0.0f, 1.0f);
	glRotatef(sceneNode->elevation, 0.0f, 1.0f, 0.0f);
	glRotatef(90, 0.0f, 1.0f, 0.0f);

	glDisable(GL_TEXTURE_2D);
	//glEnable(GL_LIGHTING);

	fvec4 noseColor = sceneNode->teamColor;
	fvec4 finColor(noseColor.r*0.5f,noseColor.g*0.5f,noseColor.b*0.5f,1);
	fvec4 coneColor(0.125f,0.125f,0.125f,1);
	fvec4 bodyColor(1,1,1,1);

	glPushMatrix();

	GLUquadric *q = gluNewQuadric();

	glColor4f(noseColor.r,noseColor.g,noseColor.b,1.0f);
	glTranslatef(0, 0, gmMissleSize);
	glRotatef((float)TimeKeeper::getCurrent().getSeconds() * rotSpeed,0,0,1);

	// nosecone
	gluDisk(q,0,noseRad,slices,1);
	glTranslatef(0, 0, -noseLen);
	gluCylinder(q,maxRad,noseRad,noseLen,slices,1);
	addTriangleCount(slices * 2);

	// body
	myColor4fv(bodyColor);
	glTranslatef(0, 0, -bodyLen);
	gluCylinder(q,maxRad,maxRad,bodyLen,slices,1);
	addTriangleCount(slices);

	glTranslatef(0, 0, -bevelLen);
	gluCylinder(q,waistRad,maxRad,bevelLen,slices,1);
	addTriangleCount(slices);

	// waist
	myColor4fv(coneColor);
	glTranslatef(0, 0, -waistLen);
	gluCylinder(q,waistRad,waistRad,waistLen,slices,1);
	addTriangleCount(slices);

	// booster
	myColor3fv(bodyColor);
	glTranslatef(0, 0, -bevelLen);
	gluCylinder(q,maxRad,waistRad,bevelLen,slices,1);
	addTriangleCount(slices);

	glTranslatef(0, 0, -boosterLen);
	gluCylinder(q,maxRad,maxRad,boosterLen,slices,1);
	addTriangleCount(slices);

	glTranslatef(0, 0, -bevelLen);
	gluCylinder(q,waistRad,maxRad,bevelLen,slices,1);
	addTriangleCount(slices);

	// engine
	myColor3fv(coneColor);
	glTranslatef(0, 0, -engineLen);
	gluCylinder(q,engineRad,waistRad,engineLen,slices,1);
	addTriangleCount(slices);

	// fins
	myColor3fv(finColor);
	glTranslatef(0, 0, engineLen + bevelLen);

	for ( int i = 0; i < 4; i++)
	{
		glRotatef(i*90.0f,0,0,1);
		drawFin ( maxRad, finRadius, boosterLen, finForeDelta, finCapSize);
	}

	glPopMatrix();

	gluDeleteQuadric(q);

	glEnable(GL_TEXTURE_2D);
	//glDisable(GL_LIGHTING);

	glPopMatrix();

	glDepthMask(GL_FALSE);
}


void BoltSceneNode::BoltRenderNode::renderGeoBolt()
{
	// bzdb these 2? they control the shot size
	float lenMod = 0.0675f + (BZDBCache::shotLength * 0.0125f);
	float baseRadius = 0.225f;

	float len = sceneNode->length * lenMod;
	glPushMatrix();
	glRotatef(sceneNode->azimuth, 0.0f, 0.0f, 1.0f);
	glRotatef(sceneNode->elevation, 0.0f, 1.0f, 0.0f);
	glRotatef(90, 0.0f, 1.0f, 0.0f);

	float alphaMod = 1.0f;
//	if (sceneNode->phasingShot)
	//	alphaMod = 0.85f;

	glDisable(GL_TEXTURE_2D);

	float coreBleed = 4.5f;
	float minimumChannelVal = 0.45f;

	fvec3 coreColor;
	coreColor.r =  sceneNode->color[0] * coreBleed;
	coreColor.g =  sceneNode->color[1] * coreBleed;
	coreColor.b =  sceneNode->color[2] * coreBleed;
	if (coreColor.r < minimumChannelVal) { coreColor.r = minimumChannelVal; }
	if (coreColor.g < minimumChannelVal) { coreColor.g = minimumChannelVal; }
	if (coreColor.b < minimumChannelVal) { coreColor.b = minimumChannelVal; }

	myColor4fv(fvec4(coreColor, 0.85f * alphaMod));
	renderGeoPill(baseRadius,len,16);

	float radInc = 1.5f * baseRadius - baseRadius;
	glPushMatrix();
	glTranslatef(0, 0, -radInc * 0.5f);
	fvec4 c;
	c.x = sceneNode->color[0];
	c.y = sceneNode->color[1];
	c.z = sceneNode->color[2];
	c.w = 0.5f;

	myColor4fv(c);
	renderGeoPill(1.5f * baseRadius, len + radInc, 25);
	glPopMatrix();

	radInc = 2.7f * baseRadius - baseRadius;
	glPushMatrix();
	glTranslatef(0, 0, -radInc*0.5f);
	c.w = 0.25f;
	myColor4fv(c);
	renderGeoPill(2.7f * baseRadius, len + radInc, 32);
	glPopMatrix();

	radInc = 3.8f * baseRadius - baseRadius;
	glPushMatrix();
	glTranslatef(0, 0,-radInc*0.5f);
	c.w = 0.125f;
	myColor4fv(c);
	renderGeoPill(3.8f * baseRadius, len + radInc, 48);
	glPopMatrix();

	glEnable(GL_TEXTURE_2D);

	glPopMatrix();
}


void BoltSceneNode::BoltRenderNode::renderGeoPill(float radius, float len,
												  int segments, float endRad)
{
	glPushMatrix();

	float assRadius = radius;
	if (endRad >= 0)
		assRadius = endRad;

	float lenMinusRads = len - (radius+assRadius);

	GLUquadric *q = gluNewQuadric();
	if (assRadius > 0)
	{
		// 4 parts of the first hemisphere
		gluCylinder(q,0,assRadius*0.43589,assRadius*0.1f,segments,1);
		addTriangleCount(segments);
		glTranslatef(0,0,assRadius*0.1f);

		gluCylinder(q,assRadius*0.43589,assRadius*0.66144,assRadius*0.15f,segments,1);
		addTriangleCount(segments);
		glTranslatef(0,0,assRadius*0.15f);

		gluCylinder(q,assRadius*0.66144f,assRadius*0.86603f,assRadius*0.25f,segments,1);
		addTriangleCount(segments);
		glTranslatef(0,0,assRadius*0.25f);

		gluCylinder(q,assRadius*0.86603,assRadius,assRadius*0.5f,segments,1);
		addTriangleCount(segments);
		glTranslatef(0,0,assRadius*0.5f);
	}

	// the "shaft"
	if (lenMinusRads > 0)
	{
		gluCylinder(q,assRadius,radius,lenMinusRads,segments,1);
		addTriangleCount(segments);
		glTranslatef(0,0,lenMinusRads);
	}

	if (radius > 0)
	{
		// 4 parts of the last hemisphere
		gluCylinder(q,radius,radius*0.86603,radius*0.5f,segments,1);
		addTriangleCount(segments);
		glTranslatef(0,0,radius*0.5f);

		gluCylinder(q,radius*0.86603f,radius*0.66144f,radius*0.25f,segments,1);
		addTriangleCount(segments);
		glTranslatef(0,0,radius*0.25f);

		gluCylinder(q,radius*0.66144,radius*0.43589,radius*0.15f,segments,1);
		addTriangleCount(segments);
		glTranslatef(0,0,radius*0.15f);

		gluCylinder(q,radius*0.43589,0,radius*0.1f,segments,1);
		addTriangleCount(segments);
		glTranslatef(0,0,radius*0.1f);
	}

	gluDeleteQuadric(q);
	glPopMatrix();
}

void			BoltSceneNode::BoltRenderNode::render()
{
	if (sceneNode->invisible) {
		return;
	}
	const float radius = sceneNode->size;
	const int   shotLength = (int)(BZDBCache::shotLength * 3.0f);
	const bool  experimental = (RENDERER.useQuality() >= 3);

	const bool blackFog = RENDERER.isFogActive() && BZDBCache::blend &&
		((shotLength > 0) || experimental);
	if (blackFog) {
		glFogfv(GL_FOG_COLOR, fvec4(0.0f, 0.0f, 0.0f, 0.0f));
	}

	const float* sphere = sceneNode->getSphere();
	glPushMatrix();
	glTranslatef(sphere[0], sphere[1], sphere[2]);

	bool drawBillboardShot = false;
	if (experimental){
		if (sceneNode->isSuper)
			renderGeoBolt();
		else{
			if (sceneNode->drawFlares){
				if (BZDBCache::shotLength > 0)
					renderGeoGMBolt();
				drawBillboardShot = true;
			}
			else
				drawBillboardShot = true;
		}
	}
	else
		drawBillboardShot = true;

	if (drawBillboardShot){
		RENDERER.getViewFrustum().executeBillboard();
		glScalef(radius, radius, radius);
		// draw some flares
		if (sceneNode->drawFlares) {
			if (!RENDERER.isSameFrame()) {
				numFlares = 3 + int(3.0f * (float)bzfrand());
				for (int i = 0; i < numFlares; i++) {
					theta[i] = (float)(2.0 * M_PI * bzfrand());
					phi[i] = (float)bzfrand() - 0.5f;
					phi[i] *= (float)(2.0 * M_PI * fabsf(phi[i]));
				}
			}

			if (sceneNode->texturing) glDisable(GL_TEXTURE_2D);
			myColor4fv(flareColor);
			if (!BZDBCache::blend) myStipple(flareColor[3]);
			for (int i = 0; i < numFlares; i++) {
				// pick random direction in 3-space.  picking a random theta with
				// a uniform distribution is fine, but doing so with phi biases
				// the directions toward the poles.  my correction doesn't remove
				// the bias completely, but moves it towards the equator, which is
				// really where i want it anyway cos the flares are more noticeable
				// there.
				const float c = FlareSize * cosf(phi[i]);
				const float s = FlareSize * sinf(phi[i]);
				const float ti = theta[i];
				const float fs = FlareSpread;

				GLfloat drawArray[] = {
					core[0][0], core[0][1], core[1][0], // this was a glVertex3fv(core[0]) before, but core[x] has only 2 elements...???
					c * cosf(ti - fs), c * sinf(ti - fs), s,
					c * cosf(ti) * 2.0f, c * sinf(ti) * 2.0f, s * 2.0f,
					c * cosf(ti + fs),   c * sinf(ti + fs),   s
				};

				glDisableClientState(GL_COLOR_ARRAY);
				glDisableClientState(GL_TEXTURE_COORD_ARRAY);
				glDisableClientState(GL_NORMAL_ARRAY);
				glEnableClientState(GL_VERTEX_ARRAY);

				glVertexPointer(3, GL_FLOAT, 0, drawArray);

				glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
			}
			if (sceneNode->texturing) glEnable(GL_TEXTURE_2D);

			addTriangleCount(numFlares * 2);
		}

		if (sceneNode->texturing) {
			// draw billboard square
			const float u0 = (float)u * du;
			const float v0 = (float)v * dv;
			const float u1 = u0 + du;
			const float v1 = v0 + dv;
			myColor4fv(textureColor); // 1.0f all

			GLfloat drawArray[] = {
				u0, v0,
				-1.0f, -1.0f,
				u1, v0,
				1.0f, -1.0f,
				u1, v1,
				1.0f, 1.0f,
				u0, v1,
				-1.0f, +1.0f
			};

			glDisableClientState(GL_COLOR_ARRAY);
			glEnableClientState(GL_TEXTURE_COORD_ARRAY);
			glDisableClientState(GL_NORMAL_ARRAY);
			glEnableClientState(GL_VERTEX_ARRAY);

			glTexCoordPointer(2, GL_FLOAT, 4 * sizeof(GLfloat), drawArray);
			glVertexPointer(2, GL_FLOAT, 4 * sizeof(GLfloat), drawArray + 2);

			glDrawArrays(GL_TRIANGLE_FAN, 0, 4);

			addTriangleCount(2);

			// draw shot trail  (more billboarded quads)
			if ((shotLength > 0) && (sceneNode->length > 1.0e-6f)) {
				const float startSize  = 0.6f;
				const float startAlpha = 0.8f;

				GLint lastTextureBinding;
				glGetIntegerv(GL_TEXTURE_BINDING_2D, &lastTextureBinding);
				TextureManager &tm = TextureManager::instance();
				const int texID = tm.getTextureID("shot_tail");
				const ImageInfo& texInfo = tm.getInfo(texID);
				if (texInfo.id >= 0) {
					texInfo.texture->execute();
				}

				fvec3 vel(sceneNode->velocity[0],sceneNode->velocity[1],sceneNode->velocity[2]);
				const fvec3  dir = vel * (-1.0f / sceneNode->length);

				const float invLenPlusOne = 1.0f / (float)(shotLength + 1);
				const float shiftScale = 90.0f / (150.0f + (float)shotLength);
				float Size = sceneNode->size * startSize;
				float alpha = startAlpha;
				const float sizeStep  = Size  * invLenPlusOne;
				const float alphaStep = alpha * invLenPlusOne;

				fvec3 pos;
				pos.x = sphere[0];
				pos.y = sphere[1];
				pos.z = sphere[2];

				int uvCell = rand() % 16;

				for (int i = 0; i < shotLength; i++) {
					Size  -= sizeStep;
					const float s = Size * (0.65f + (1.0f * (float)bzfrand()));
					const float shift = s * shiftScale;

					pos += (shift * dir);
					if (pos.z < 0.0f) {
						continue;
					}

					uvCell = (uvCell + 1) % 16;
					const float U0 = (uvCell % 4 ) * 0.25f;
					const float V0 = (uvCell / 4 ) * 0.25f;
					const float U1 = U0 + 0.25f;
					const float V1 = V0 + 0.25f;

					alpha -= alphaStep;
					glColor4f(mainColor[0],mainColor[1],mainColor[2], alpha);
					glPopMatrix();
					glPushMatrix();

					glTranslatef(pos.x, pos.y, pos.z);
					RENDERER.getViewFrustum().executeBillboard();
					glScalef(s, s, s);

					GLfloat drawArray2[] = {
						U0, V0,
						-1.0f, -1.0f,
						U1, V0,
						1.0f, -1.0f,
						U1, V1,
						1.0f, 1.0f,
						U0, V1,
						-1.0f, 1.0f
					};

					glDisableClientState(GL_COLOR_ARRAY);
					glEnableClientState(GL_TEXTURE_COORD_ARRAY);
					glDisableClientState(GL_NORMAL_ARRAY);
					glEnableClientState(GL_VERTEX_ARRAY);

					glTexCoordPointer(2, GL_FLOAT, 4 * sizeof(GLfloat), drawArray2);
					glVertexPointer(2, GL_FLOAT, 4 * sizeof(GLfloat), drawArray2 + 2);

					glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
				}

				addTriangleCount(shotLength * 2);
				glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
				glBindTexture(GL_TEXTURE_2D, (GLuint) lastTextureBinding); // revert the texture
			}
		}
		else if (BZDBCache::blend) {
			// draw corona
			GLfloat drawArray[] = {
				mainColor[0], mainColor[1], mainColor[2], mainColor[3],
				core[1][0], core[1][1],

				outerColor[0], outerColor[1], outerColor[2], outerColor[3],
				corona[0][0], corona[0][1],

				outerColor[0], outerColor[1], outerColor[2], outerColor[3],
				corona[1][0], corona[1][1],


				outerColor[0], outerColor[1], outerColor[2], outerColor[3],
				corona[1][0], corona[1][1],

				mainColor[0], mainColor[1], mainColor[2], mainColor[3],
				core[2][0], core[2][1],

				mainColor[0], mainColor[1], mainColor[2], mainColor[3],
				core[1][0], core[1][1],


				mainColor[0], mainColor[1], mainColor[2], mainColor[3],
				core[2][0], core[2][1],

				outerColor[0], outerColor[1], outerColor[2], outerColor[3],
				corona[1][0], corona[1][1],

				outerColor[0], outerColor[1], outerColor[2], outerColor[3],
				corona[2][0], corona[2][1],


				outerColor[0], outerColor[1], outerColor[2], outerColor[3],
				corona[2][0], corona[2][1],

				mainColor[0], mainColor[1], mainColor[2], mainColor[3],
				core[3][0], core[3][1],

				mainColor[0], mainColor[1], mainColor[2], mainColor[3],
				core[2][0], core[2][1],


				mainColor[0], mainColor[1], mainColor[2], mainColor[3],
				core[3][0], core[3][1],

				outerColor[0], outerColor[1], outerColor[2], outerColor[3],
				corona[2][0], corona[2][1],

				outerColor[0], outerColor[1], outerColor[2], outerColor[3],
				corona[3][0], corona[3][1],


				outerColor[0], outerColor[1], outerColor[2], outerColor[3],
				corona[3][0], corona[3][1],

				mainColor[0], mainColor[1], mainColor[2], mainColor[3],
				core[4][0], core[4][1],

				mainColor[0], mainColor[1], mainColor[2], mainColor[3],
				core[3][0], core[3][1],


				mainColor[0], mainColor[1], mainColor[2], mainColor[3],
				core[4][0], core[4][1],

				outerColor[0], outerColor[1], outerColor[2], outerColor[3],
				corona[3][0], corona[3][1],

				outerColor[0], outerColor[1], outerColor[2], outerColor[3],
				corona[4][0], corona[4][1],


				outerColor[0], outerColor[1], outerColor[2], outerColor[3],
				corona[4][0], corona[4][1],

				mainColor[0], mainColor[1], mainColor[2], mainColor[3],
				core[5][0], core[5][1],

				mainColor[0], mainColor[1], mainColor[2], mainColor[3],
				core[4][0], core[4][1],


				mainColor[0], mainColor[1], mainColor[2], mainColor[3],
				core[5][0], core[5][1],

				outerColor[0], outerColor[1], outerColor[2], outerColor[3],
				corona[4][0], corona[4][1],

				outerColor[0], outerColor[1], outerColor[2], outerColor[3],
				corona[5][0], corona[5][1],


				outerColor[0], outerColor[1], outerColor[2], outerColor[3],
				corona[5][0], corona[5][1],

				mainColor[0], mainColor[1], mainColor[2], mainColor[3],
				core[6][0], core[6][1],

				mainColor[0], mainColor[1], mainColor[2], mainColor[3],
				core[5][0], core[5][1],


				mainColor[0], mainColor[1], mainColor[2], mainColor[3],
				core[6][0], core[6][1],

				outerColor[0], outerColor[1], outerColor[2], outerColor[3],
				corona[5][0], corona[5][1],

				outerColor[0], outerColor[1], outerColor[2], outerColor[3],
				corona[6][0], corona[6][1],


				outerColor[0], outerColor[1], outerColor[2], outerColor[3],
				corona[6][0], corona[6][1],

				mainColor[0], mainColor[1], mainColor[2], mainColor[3],
				core[7][0], core[7][1],

				mainColor[0], mainColor[1], mainColor[2], mainColor[3],
				core[6][0], core[6][1],


				mainColor[0], mainColor[1], mainColor[2], mainColor[3],
				core[7][0], core[7][1],

				outerColor[0], outerColor[1], outerColor[2], outerColor[3],
				corona[6][0], corona[6][1],

				outerColor[0], outerColor[1], outerColor[2], outerColor[3],
				corona[7][0], corona[7][1],


				outerColor[0], outerColor[1], outerColor[2], outerColor[3],
				corona[7][0], corona[7][1],

				mainColor[0], mainColor[1], mainColor[2], mainColor[3],
				core[8][0], core[8][1],

				mainColor[0], mainColor[1], mainColor[2], mainColor[3],
				core[7][0], core[7][1],


				mainColor[0], mainColor[1], mainColor[2], mainColor[3],
				core[8][0], core[8][1],

				outerColor[0], outerColor[1], outerColor[2], outerColor[3],
				corona[7][0], corona[7][1],

				outerColor[0], outerColor[1], outerColor[2], outerColor[3],
				corona[0][0], corona[0][1],


				outerColor[0], outerColor[1], outerColor[2], outerColor[3],
				corona[0][0], corona[0][1],

				mainColor[0], mainColor[1], mainColor[2], mainColor[3],
				core[1][0], core[1][1],

				mainColor[0], mainColor[1], mainColor[2], mainColor[3],
				core[8][0], core[8][1]
			};

			glEnableClientState(GL_COLOR_ARRAY);
			glDisableClientState(GL_TEXTURE_COORD_ARRAY);
			glDisableClientState(GL_NORMAL_ARRAY);
			glEnableClientState(GL_VERTEX_ARRAY);

			glColorPointer(4, GL_FLOAT, 6 * sizeof(GLfloat), drawArray);
			glVertexPointer(2, GL_FLOAT, 6 * sizeof(GLfloat), drawArray + 4);

			glDrawArrays(GL_TRIANGLES, 0, 48);

			// draw core
			GLfloat drawArray2[] = {
				innerColor[0], innerColor[1], innerColor[2], innerColor[3],
				core[0][0], core[0][1],
				mainColor[0], mainColor[1], mainColor[2], mainColor[3],
				core[1][0], core[1][1],
				mainColor[0], mainColor[1], mainColor[2], mainColor[3],
				core[2][0], core[2][1],
				mainColor[0], mainColor[1], mainColor[2], mainColor[3],
				core[3][0], core[3][1],
				mainColor[0], mainColor[1], mainColor[2], mainColor[3],
				core[4][0], core[4][1],
				mainColor[0], mainColor[1], mainColor[2], mainColor[3],
				core[5][0], core[5][1],
				mainColor[0], mainColor[1], mainColor[2], mainColor[3],
				core[6][0], core[6][1],
				mainColor[0], mainColor[1], mainColor[2], mainColor[3],
				core[7][0], core[7][1],
				mainColor[0], mainColor[1], mainColor[2], mainColor[3],
				core[8][0], core[8][1],
				mainColor[0], mainColor[1], mainColor[2], mainColor[3],
				core[1][0], core[1][1]
			};

			glEnableClientState(GL_COLOR_ARRAY);
			glDisableClientState(GL_TEXTURE_COORD_ARRAY);
			glDisableClientState(GL_NORMAL_ARRAY);
			glEnableClientState(GL_VERTEX_ARRAY);

			glColorPointer(4, GL_FLOAT, 6 * sizeof(GLfloat), drawArray2);
			glVertexPointer(2, GL_FLOAT, 6 * sizeof(GLfloat), drawArray2 + 4);

			glDrawArrays(GL_TRIANGLE_FAN, 0, 10);

			addTriangleCount(24);
		}
		else {
			// draw corona
			myColor4fv(coronaColor);
			myStipple(coronaColor[3]);

			GLfloat drawArray[] = {
				core[1][0], core[1][1],
				corona[0][0], corona[0][1],
				corona[1][0], corona[1][1],

				corona[1][0], corona[1][1],
				core[2][0], core[2][1],
				core[1][0], core[1][1],

				core[2][0], core[2][1],
				corona[1][0], corona[1][1],
				corona[2][0], corona[2][1],

				corona[2][0], corona[2][1],
				core[3][0], core[3][1],
				core[2][0], core[2][1],

				core[3][0], core[3][1],
				corona[2][0], corona[2][1],
				corona[3][0], corona[3][1],

				corona[3][0], corona[3][1],
				core[4][0], core[4][1],
				core[3][0], core[3][1],

				core[4][0], core[4][1],
				corona[3][0], corona[3][1],
				corona[4][0], corona[4][1],

				corona[4][0], corona[4][1],
				core[5][0], core[5][1],
				core[4][0], core[4][1],

				core[5][0], core[5][1],
				corona[4][0], corona[4][1],
				corona[5][0], corona[5][1],

				corona[5][0], corona[5][1],
				core[6][0], core[6][1],
				core[5][0], core[5][1],

				core[6][0], core[6][1],
				corona[5][0], corona[5][1],
				corona[6][0], corona[6][1],

				corona[6][0], corona[6][1],
				core[7][0], core[7][1],
				core[6][0], core[6][1],

				core[7][0], core[7][1],
				corona[6][0], corona[6][1],
				corona[7][0], corona[7][1],

				corona[7][0], corona[7][1],
				core[8][0], core[8][1],
				core[7][0], core[7][1],

				core[8][0], core[8][1],
				corona[7][0], corona[7][1],
				corona[0][0], corona[0][1],

				corona[0][0], corona[0][1],
				core[1][0], core[1][1],
				core[8][0], core[8][1]
			};

			glDisableClientState(GL_COLOR_ARRAY);
			glDisableClientState(GL_TEXTURE_COORD_ARRAY);
			glDisableClientState(GL_NORMAL_ARRAY);
			glEnableClientState(GL_VERTEX_ARRAY);

			glVertexPointer(2, GL_FLOAT, 0, drawArray);

			glDrawArrays(GL_TRIANGLES, 0, 48);

			// draw core
			myStipple(1.0f);

			GLfloat drawArray2[] = {
				innerColor[0], innerColor[1], innerColor[2], innerColor[3],
				core[0][0], core[0][1],
				mainColor[0], mainColor[1], mainColor[2], mainColor[3],
				core[1][0], core[1][1],
				mainColor[0], mainColor[1], mainColor[2], mainColor[3],
				core[2][0], core[2][1],
				mainColor[0], mainColor[1], mainColor[2], mainColor[3],
				core[3][0], core[3][1],
				mainColor[0], mainColor[1], mainColor[2], mainColor[3],
				core[4][0], core[4][1],
				mainColor[0], mainColor[1], mainColor[2], mainColor[3],
				core[5][0], core[5][1],
				mainColor[0], mainColor[1], mainColor[2], mainColor[3],
				core[6][0], core[6][1],
				mainColor[0], mainColor[1], mainColor[2], mainColor[3],
				core[7][0], core[7][1],
				mainColor[0], mainColor[1], mainColor[2], mainColor[3],
				core[8][0], core[8][1],
				mainColor[0], mainColor[1], mainColor[2], mainColor[3],
				core[1][0], core[1][1]
			};

			glEnableClientState(GL_COLOR_ARRAY);
			glDisableClientState(GL_TEXTURE_COORD_ARRAY);
			glDisableClientState(GL_NORMAL_ARRAY);
			glEnableClientState(GL_VERTEX_ARRAY);

			glColorPointer(4, GL_FLOAT, 6 * sizeof(GLfloat), drawArray2);
			glVertexPointer(2, GL_FLOAT, 6 * sizeof(GLfloat), drawArray2 + 4);

			glDrawArrays(GL_TRIANGLE_FAN, 0, 10);

			myStipple(0.5f);

			addTriangleCount(24);
		}

	}

	glPopMatrix();

	if (blackFog) {
		glFogfv(GL_FOG_COLOR, RENDERER.getFogColor());
	}

	if (RENDERER.isLastFrame()) {
		if (++u == cu) {
			u = 0;
			if (++v == cv) {
				v = 0;
			}
		}
	}
}

// Local Variables: ***
// mode:C++ ***
// tab-width: 8 ***
// c-basic-offset: 2 ***
// indent-tabs-mode: t ***
// End: ***
// ex: shiftwidth=2 tabstop=8
